//! 事件去重管理器

use crate::event::HttpEvent;
use crate::ScanError;
use chrono::{DateTime, Utc, Duration};
use moka::future::Cache;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
use tokio::sync::RwLock;

/// 去重记录
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DedupRecord {
    /// 事件唯一键
    pub key: String,
    /// 首次发现时间
    pub first_seen: DateTime<Utc>,
    /// 最后出现时间
    pub last_seen: DateTime<Utc>,
    /// 出现次数
    pub count: u64,
    /// 关联的指纹
    pub fingerprint: Option<String>,
    /// 是否已处理
    pub processed: bool,
}

impl DedupRecord {
    /// 创建新的去重记录
    pub fn new(key: String, fingerprint: Option<String>) -> Self {
        let now = Utc::now();
        Self {
            key,
            first_seen: now,
            last_seen: now,
            count: 1,
            fingerprint,
            processed: false,
        }
    }

    /// 更新记录
    pub fn update(&mut self) {
        self.last_seen = Utc::now();
        self.count += 1;
    }

    /// 检查是否过期
    pub fn is_expired(&self, ttl: Duration) -> bool {
        self.last_seen + ttl < Utc::now()
    }
}

/// 去重配置
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DedupConfig {
    /// 缓存TTL (默认5分钟)
    pub ttl: Duration,
    /// 最大缓存条目数 (默认10000)
    pub max_entries: u64,
    /// 是否启用指纹去重
    pub enable_fingerprint_dedup: bool,
    /// 清理任务间隔 (默认1分钟)
    pub cleanup_interval: Duration,
}

impl Default for DedupConfig {
    fn default() -> Self {
        Self {
            ttl: Duration::minutes(5),
            max_entries: 10_000,
            enable_fingerprint_dedup: true,
            cleanup_interval: Duration::minutes(1),
        }
    }
}

/// 去重管理器
pub struct DedupManager {
    /// 内存缓存
    cache: Arc<Cache<String, DedupRecord>>,
    /// 配置
    config: DedupConfig,
    /// 清理任务句柄
    cleanup_handle: Arc<RwLock<Option<tokio::task::JoinHandle<()>>>>,
}

impl DedupManager {
    /// 创建新的去重管理器
    pub fn new(config: DedupConfig) -> Self {
        let cache = Cache::builder()
            .time_to_live(config.ttl.to_std().unwrap_or(std::time::Duration::from_secs(300)))
            .max_capacity(config.max_entries)
            .build();

        let manager = Self {
            cache: Arc::new(cache),
            config,
            cleanup_handle: Arc::new(RwLock::new(None)),
        };

        manager.start_cleanup_task();
        manager
    }

    /// 启动清理任务
    fn start_cleanup_task(&self) {
        let cache = self.cache.clone();
        let cleanup_interval = self.config.cleanup_interval;

        let handle = tokio::spawn(async move {
            let mut interval = tokio::time::interval(cleanup_interval.to_std().unwrap_or(std::time::Duration::from_secs(60)));
            loop {
                interval.tick().await;
                // 由于使用了moka的TTL，这里主要是记录统计信息
                // 简化版本，只记录缓存大小
                tracing::debug!(
                    "Dedup cache stats: size={}",
                    cache.entry_count()
                );
            }
        });

        // 使用 try_write 而不是 blocking_write 避免在异步上下文中阻塞
        if let Ok(mut handle_guard) = self.cleanup_handle.try_write() {
            *handle_guard = Some(handle);
        }
    }

    /// 检查事件是否重复
    pub async fn is_duplicate(&self, event: &HttpEvent, fingerprint: Option<String>) -> bool {
        let key = self.generate_dedup_key(event, fingerprint.as_deref());

        match self.cache.get(&key) {
            Some(mut record) => {
                // 更新记录
                record.update();
                let _ = self.cache.insert(key, record).await;
                true
            }
            None => {
                // 创建新记录
                let record = DedupRecord::new(key.clone(), fingerprint);
                let _ = self.cache.insert(key, record).await;
                false
            }
        }
    }

    /// 生成去重键
    fn generate_dedup_key(&self, event: &HttpEvent, fingerprint: Option<&str>) -> String {
        if self.config.enable_fingerprint_dedup {
            if let Some(fp) = fingerprint {
                // 基于指纹的去重
                format!("fp:{}:{}", event.metadata.host, fp)
            } else {
                // 基于事件内容的去重
                format!("event:{}", event.get_unique_key())
            }
        } else {
            // 简单基于事件内容的去重
            format!("event:{}", event.get_unique_key())
        }
    }

    /// 标记事件为已处理
    pub async fn mark_processed(&self, event: &HttpEvent, fingerprint: Option<String>) -> Result<(), ScanError> {
        let key = self.generate_dedup_key(event, fingerprint.as_deref());
        if let Some(mut record) = self.cache.get(&key) {
            record.processed = true;
            let _ = self.cache.insert(key, record).await;
        }

        Ok(())
    }

    /// 获取去重统计信息
    pub async fn get_stats(&self) -> DedupStats {
        // 简化统计信息，因为moka::future::Cache可能不支持metrics()方法
        DedupStats {
            total_entries: self.cache.entry_count(),
            hit_count: 0,  // 暂时返回0
            miss_count: 0, // 暂时返回0
            hit_rate: 0.0, // 暂时返回0
        }
    }

    /// 清理过期条目
    pub async fn cleanup_expired(&self) -> usize {
        // moka会自动清理过期条目，这里返回0表示无需手动清理
        0
    }

    /// 获取特定键的记录
    pub async fn get_record(&self, key: &str) -> Option<DedupRecord> {
        self.cache.get(key)
    }

    /// 手动添加记录
    pub async fn add_record(&self, record: DedupRecord) -> Result<(), ScanError> {
        let _ = self.cache.insert(record.key.clone(), record).await;
        Ok(())
    }
}

impl Drop for DedupManager {
    fn drop(&mut self) {
        // 停止清理任务
        if let Ok(mut handle_guard) = self.cleanup_handle.try_write() {
            if let Some(handle) = handle_guard.take() {
                handle.abort();
            }
        }
    }
}

/// 去重统计信息
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct DedupStats {
    /// 总条目数
    pub total_entries: u64,
    /// 命中次数
    pub hit_count: u64,
    /// 未命中次数
    pub miss_count: u64,
    /// 命中率
    pub hit_rate: f64,
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::event::{HttpEvent, EventType};
    use http::{Method, StatusCode, Uri};
    use std::collections::HashMap;

    #[tokio::test]
    async fn test_dedup_basic() {
        let config = DedupConfig {
            ttl: Duration::seconds(1),
            max_entries: 100,
            enable_fingerprint_dedup: false,
            cleanup_interval: Duration::seconds(10),
        };

        let dedup = DedupManager::new(config);

        // 创建测试事件
        let event = create_test_event();

        // 第一次检查不应该重复
        assert!(!dedup.is_duplicate(&event, None).await);

        // 第二次检查应该重复
        assert!(dedup.is_duplicate(&event, None).await);

        // 检查统计信息
        let stats = dedup.get_stats().await;
        assert!(stats.hit_count > 0);
    }

    #[tokio::test]
    async fn test_fingerprint_dedup() {
        let config = DedupConfig {
            ttl: Duration::minutes(5),
            max_entries: 1000,
            enable_fingerprint_dedup: true,
            cleanup_interval: Duration::minutes(1),
        };

        let dedup = DedupManager::new(config);
        let event = create_test_event();

        // 基于指纹的去重
        let fingerprint = "nginx/1.18.0".to_string();

        assert!(!dedup.is_duplicate(&event, Some(fingerprint.clone())).await);
        assert!(dedup.is_duplicate(&event, Some(fingerprint)).await);
    }

    fn create_test_event() -> HttpEvent {
        let mut headers = HashMap::new();
        headers.insert("host".to_string(), "example.com".to_string());

        HttpEvent::new_request(
            Method::GET,
            "https://example.com/test".parse::<Uri>().unwrap(),
            headers,
            b"test body",
            "127.0.0.1".to_string(),
            "conn-123".to_string(),
        )
    }
}